/*
 * Copyright (c) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
*/


#include"geofence_track.h"
#include <string>
#include <cerrno>
#include <sys/epoll.h>
#include <sys/timerfd.h>
#include <linux/netlink.h>
#include <unistd.h>
#include "dummy_values_bucket.h"
#include "geofence_common.h"


namespace OHOS {
namespace Msdp {

const int32_t TIMER_INTERVAL = 30;
const int32_t ERR_INVALID_FD = -1;
const int32_t TIMES_UNSLEEP = 0;
const int32_t TIMES_SLEEP = 40;
std::string cellCache_;

void GeofenceTrack::GeofenceCallbackDeathRecipient::OnRemoteDied(const wptr<IRemoteObject>& remote)
{
    if (remote == nullptr) {
        GEOFENCE_HILOGE(GEOFENCE_MODULE_SERVICE, "OnRemoteDied failed, remote is nullptr");
        return;
    }
    GEOFENCE_HILOGD(GEOFENCE_MODULE_SERVICE, "Recv death notice");
}

bool GeofenceTrack::Init()
{
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "GeofenceStateMachine: Init start");
    if (geofenceCBDeathRecipient_ == nullptr) {
        geofenceCBDeathRecipient_ = new GeofenceCallbackDeathRecipient();
    }
    trackTestFlag_ = geofenceConfig_->GetTrackTestFlagConf();

    geofenceImplClient_ = std::make_unique<GeofenceImplClient>();

    InitTimer();
    StartThread();

    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "GeofenceStateMachine: Init success");
    return true;
}

void GeofenceTrack::InitTimer()
{
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "enter");
    epFd_ = epoll_create1(EPOLL_CLOEXEC);
    if (epFd_ == -1) {
        GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "create epoll fd fail.");
    }
    timerFd_ = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK);
    if (timerFd_ == ERR_INVALID_FD) {
        GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "create timer fd fail.");
    }
    SetTimerInterval(TIMER_INTERVAL);
    fcntl(timerFd_, F_SETFL, O_NONBLOCK);
    callbacks_.insert(std::make_pair(timerFd_, &GeofenceTrack::TimerCallback));
    if (RegisterTimerCallback(timerFd_, EVENT_TIMER_FD)) {
        GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "register timer fd fail.");
    }
}

void GeofenceTrack::SetTimerInterval(int interval)
{
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "enter");
    struct itimerspec itval;

    if (timerFd_ == ERR_INVALID_FD) {
        return;
    }

    timerInterval_ = interval;

    if (interval < 0) {
        interval = 0;
    }

    itval.it_interval.tv_sec = interval;
    itval.it_interval.tv_nsec = 0;
    itval.it_value.tv_sec = interval;
    itval.it_value.tv_nsec = 0;

    if (timerfd_settime(timerFd_, 0, &itval, nullptr) == -1) {
        GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "set timer failed");
    }

    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "enter");
    return;
}

void GeofenceTrack::TimerCallback()
{
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "enter");
    unsigned long long timers;
    if (read(timerFd_, &timers, sizeof(timers)) == -1) {
        GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "read timer fd fail.");
        return;
    }
    SetTrack();
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "exit");
}

int GeofenceTrack::RegisterTimerCallback(const int fd, const EventType et)
{
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "enter");
    struct epoll_event ev;

    ev.events = EPOLLIN;
    if (et == EVENT_TIMER_FD) {
        ev.events |= EPOLLWAKEUP;
    }

    ev.data.ptr = reinterpret_cast<void*>(this);
    ev.data.fd = fd;
    if (epoll_ctl(epFd_, EPOLL_CTL_ADD, fd, &ev) == -1) {
        GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "epoll_ctl failed, error num =%{public}d", errno);
        return -1;
    }

    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "exit");
    return 0;
}

void GeofenceTrack::StartThread()
{
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "enter");
    std::make_unique<std::thread>(&GeofenceTrack::LoopingThreadEntry, this)->detach();
}

void GeofenceTrack::LoopingThreadEntry()
{
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "enter");
    int nevents = 0;
    size_t cbct = callbacks_.size();
    struct epoll_event events[cbct];

    while (true) {
        int timeout = 0;

        nevents = epoll_wait(epFd_, events, cbct, timeout);
        if (nevents == -1) {
            continue;
        }
        for (int n = 0; n < nevents; ++n) {
            if (events[n].data.ptr) {
                GeofenceTrack *func = const_cast<GeofenceTrack *>(this);
                (callbacks_.find(events[n].data.fd)->second)(func);
            }
        }
    }
}

void GeofenceTrack::LocationCellData(std::string cell)
{
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "enter");
    cellCache_ = cell;
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "exit");
}

std::string GeofenceTrack::GetTimeNowString()
{
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "Enter");
    struct tm curTime = {0};
    std::string timeString;
    bool ret = GetSystemCurrentTime(&curTime);
    if (!ret) {
        GEOFENCE_HILOGE(GEOFENCE_MODULE_SERVICE, "Get system current time failed");
    }

    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "curTime tm_year = %{public}d", curTime.tm_year);
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "curTime tm_mon = %{public}d", curTime.tm_mon);
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "curTime tm_mday = %{public}d", curTime.tm_mday);
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "curTime tm_hour = %{public}d", curTime.tm_hour);
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "curTime tm_min = %{public}d", curTime.tm_min);
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "curTime tm_sec = %{public}d", curTime.tm_sec);
    timeString = std::to_string(curTime.tm_year) + "-" + std::to_string(curTime.tm_mon) + "-" +
        std::to_string(curTime.tm_mday) + " " + std::to_string(curTime.tm_hour) + ":" +
        std::to_string(curTime.tm_min);
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "Exit");
    return timeString;
}

void GeofenceTrack::SetTrack()
{
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "enter");
    std::string timeString;
    int32_t screenState;
    PowerMgr::PowerState ret = PowerMgr::PowerMgrClient::GetInstance().GetState();
    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "PowerState: %{public}d", ret);
    if (timeCache_ == 0) {
        if (ret == PowerMgr::PowerState::AWAKE || ret == PowerMgr::PowerState::INACTIVE){
            timeCache_ = TIMES_UNSLEEP;
        } else if (ret == PowerMgr::PowerState::SLEEP) {
            timeCache_ = TIMES_SLEEP;
        } else {
            GEOFENCE_HILOGE(GEOFENCE_MODULE_SERVICE, "exit screen state unknow");
            if (trackTestFlag_) {
                ret = PowerMgr::PowerState::AWAKE;
                GEOFENCE_HILOGE(GEOFENCE_MODULE_SERVICE, "screen state is always unknown!");
                GEOFENCE_HILOGE(GEOFENCE_MODULE_SERVICE, "test! set screen state is awake.");
            } else {
                return;
            }
        }

        screenState = int32_t(ret);
        timeString = GetTimeNowString();

        if (std::size(cellCache_) == 0) {
            GEOFENCE_HILOGE(GEOFENCE_MODULE_SERVICE, "exit, cellCache_ is null");
            return;
        }
        geofenceImplClient_->SetTrack(timeString, screenState, cellCache_);

    } else {
        timeCache_--;
        GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "exit, not reached the time since the last time set track");
    }

    GEOFENCE_HILOGI(GEOFENCE_MODULE_SERVICE, "exit");
    return;
}
} // namespace Msdp
} // n} // namespace OHOS

